package com.qushiyun.devops.admin.sync.job;

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.qushiyun.devops.admin.sync.data.po.TriggerJob;
import com.qushiyun.devops.admin.sync.job.impl.AddSyncServiceImpl;
import com.qushiyun.devops.admin.sync.job.impl.TotalSyncServiceImpl;
import com.qushiyun.devops.admin.sync.job.impl.UpdateSyncServiceImpl;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.anyline.data.jdbc.ds.DataSourceHolder;
import org.anyline.data.jdbc.runtime.JDBCRuntimeHolder;
import org.anyline.data.param.ConfigStore;
import org.anyline.data.param.init.DefaultConfigStore;
import org.anyline.entity.DataRow;
import org.anyline.entity.DataSet;
import org.anyline.service.AnylineService;
import org.springframework.stereotype.Component;

/**
 * 同步任务模版类
 *
 * @author zhouzx
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class SyncTemplate {

	private final AnylineService service;
	private final TotalSyncServiceImpl totalSyncService;
	private final AddSyncServiceImpl addSyncService;
	private final UpdateSyncServiceImpl updateSyncService;

	public static final String ADD_TENANT_ID = "ALTER TABLE %s ADD COLUMN `tenant_id` varchar(32) NULL COMMENT '租户Id' AFTER  %s";


	public synchronized void execute(long id) throws Exception {
		DataSourceHolder.setDefaultDataSource();
		ConfigStore conditions = new DefaultConfigStore();
		conditions.and("id", id);
		DataSet tasks = service.querys("sync_trigger_job", conditions);
		int syncType = tasks.getInt("trigger_type");
		if (syncType == 3) {
			conditions = new DefaultConfigStore();
			conditions.and("id", tasks.get(0, "job_ids"));
			DataSet set = service.querys("sync_trigger_job", conditions);
			for (int i = 0; i < set.size(); i++) {
				DataRow task = tasks.getRow(i);
				work(task);
			}
		} else {
			work(tasks.getRow(0));
		}
	}

	private void work(DataRow jobRow) throws Exception {
		String fromDb = "from_db", toDb = "to_db";
		boolean error = false;
		jobRow.put("status", 1);
		DataSourceHolder.setDefaultDataSource();
		TriggerJob job = new TriggerJob();
		job.setTriggerTime(new Date());
		jobRow.put("trigger_time", job.getTriggerTime());
		service.update(jobRow, "status", "trigger_time");
		try {
			long start = System.currentTimeMillis();
			log.info("{} 任务开始执行", jobRow.getString("name"));
			String tenantId = jobRow.getString("tenant_id");

			fromDb = regDb(jobRow, fromDb);
			toDb = regDb(jobRow, toDb);
			job.setId(jobRow.getLong("id"));
			job.setType(jobRow.getInt("type"));
			job.setStartIndex(jobRow.getInt("start_index", 0));
			int status = 2;

			int conditionId = jobRow.getInt("condition_id", 0);
			String fromBeforeSql = jobRow.getString("from_db_before_sql");
			String toBeforeSql = jobRow.getString("to_db_before_sql");
			DataSourceHolder.setDataSource(fromDb);
			if (StrUtil.isNotBlank(fromBeforeSql)) {
				service.execute(fromBeforeSql);
			}
			DataSourceHolder.setDataSource(toDb);
			if (StrUtil.isNotBlank(toBeforeSql)) {
				service.execute(toBeforeSql);
			}
			DataSourceHolder.setDefaultDataSource();
			DataSet condition = service.querys("sync_condition_config", "id:" + conditionId);
			DataRow conditionRow = condition.getRow(0);

			//全量
			if (1 == job.getType()) {
				totalSyncService.sync(job, fromDb, toDb, conditionRow, tenantId);
			}

			//增量
			if (2 == job.getType()) {
				addSyncService.sync(job, fromDb, toDb, conditionRow, tenantId);
			}

			//更新
			if (3 == job.getType()) {
				updateSyncService.sync(job, fromDb, toDb, conditionRow, tenantId);
			}

			String fromDbAfterSql = jobRow.getString("from_db_after_sql");
			String toDbAfterSql = jobRow.getString("to_db_after_sql");
			DataSourceHolder.setDataSource(fromDb);
			if (StrUtil.isNotBlank(fromDbAfterSql)) {
				service.execute(fromDbAfterSql);
			}
			DataSourceHolder.setDataSource(toDb);
			if (StrUtil.isNotBlank(toDbAfterSql)) {
				service.execute(toDbAfterSql);
			}

			long costTime = System.currentTimeMillis() - start;
			DataSourceHolder.setDefaultDataSource();
			jobRow.put("cost_time", costTime);
			jobRow.put("status", status);
			jobRow.put("data_num", job.getDataNum());
			jobRow.put("table_num", job.getTableNum());
			jobRow.put("trigger_time", job.getTriggerTime());
			log.info("{} 任务执行完成，耗时：{}", jobRow.getString("name"), costTime);
		} catch (Exception e) {
			String fromDbErrorSql = jobRow.getString("from_db_error_sql");
			String toDbErrorSql = jobRow.getString("to_db_error_sql");
			DataSourceHolder.setDataSource(fromDb);
			if (StrUtil.isNotBlank(fromDbErrorSql)) {
				service.execute(fromDbErrorSql);
			}
			DataSourceHolder.setDataSource(toDb);
			if (StrUtil.isNotBlank(toDbErrorSql)) {
				service.execute(toDbErrorSql);
			}
			error = true;
			throw e;
		} finally {
			if (error) {
				jobRow.put("start_index", job.getStartIndex());
				jobRow.put("status", "3");
			} else {
				jobRow.put("status", "2");
				jobRow.put("start_index", 0);
			}
			DataSourceHolder.setDefaultDataSource();
			service.update(jobRow, "status", "cost_time", "data_num", "table_num", "trigger_time", "start_index");
			JDBCRuntimeHolder.destroy(fromDb);
			JDBCRuntimeHolder.destroy(toDb);
			JDBCRuntimeHolder.destroyBean("anyline.jdbc.template." + toDb);
			JDBCRuntimeHolder.destroyBean("anyline.jdbc.template." + fromDb);
		}
	}

	private String regDb(DataRow task, String dbId) {
		DataSet from = service.querys("sync_data_source", "id:" + task.get(dbId));
		for (DataRow ds : from) {
			String code = ds.getString("id");
			dbId = code;
			String driver = ds.getString("driver");
			String url = ds.getString("url");
			String account = ds.getString("username");
			String password = ds.getString("password");
			String pool = "com.zaxxer.hikari.HikariDataSource";
			try {
				DataSourceHolder.reg(code, pool, driver, url, account, password);
				if (!"from_db".equals(code)) {
					//DataSourceHolder.validate(code);
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		return dbId;
	}

	/**
	 * json格式处理，新版anyline已经修复不需要
	 *
	 * @deprecated
	 */
	public DataSet format(DataSet set, String... cols) {
		List<DataRow> rows = set.getRows();
		Iterator<DataRow> iterator = rows.iterator();
		while (iterator.hasNext()) {
			DataRow row = iterator.next();
			if (row != null) {
				Iterator<Map.Entry<String, Object>> d = row.entrySet().iterator();
				while (d.hasNext()) {
					Map.Entry<String, Object> entry = d.next();
					Object v = entry.getValue();
					if (v instanceof List || v instanceof DataSet) {
						if (((Collection<?>) v).size() == 0) {
							row.put(entry.getKey(), "[]");
						} else {
							row.put(entry.getKey(), JSON.toJSONString(v));
						}
					}

					if (v instanceof DataRow) {
						if (((DataRow) v).size() == 0) {
							row.put(entry.getKey(), "{}");
						} else {
							row.put(entry.getKey(), JSON.toJSONString(v));
						}
					}

					if (v == null) {
						row.put(entry.getKey(), null);
					}
				}
			}
		}

		List<String> inserts = new ArrayList<>();
		if (null != cols && cols.length > 0) {
			for (String col : cols) {
				String[] temps = col.split(":");
				if (temps.length > 1) {
					if (!temps[0].equalsIgnoreCase(temps[1])) {
						set.changeKey(temps[0], temps[1], true);
					}
					inserts.add(temps[1]);
				} else {
					inserts.add(col);
				}
			}
		} else {
			return set;
		}

		return set.extract(String.valueOf(inserts));
	}
}